package ua.org.ass;

import java.util.logging.*;

/**
 * Класс для спрощення логування
 * отримує потрібний logger за назвою класу котрий викликав метод логування
 * @author Frick_ua
 */
public class Log {

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем WARNING">
	/**
	 * Залогувати повідомлення з рівнем WARNING
	 * @param msg повідомлення для логування
	 */
	public static void w(String msg) {
		w(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем WARNING
	 * @param th виключна ситуація для логування
	 */
	public static void w(Throwable th) {
		w(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем WARNING.
	 * Наприклад: <code>Log.w("Test {0}", new String[] {"warnіng."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void w(String msg, Object[] params) {
		w(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем WARNING.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void w(String msg, Throwable th) {
		w(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем WARNING.
	 * Наприклад: <code>Log.w("Test {0}", ex, new String[] {"warnіng."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void w(String msg, Throwable th, Object[] params) {
		l(Level.WARNING, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем SEVERE">
	/**
	 * Залогувати повідомлення з рівнем SEVERE
	 * @param msg повідомлення для логування
	 */
	public static void s(String msg) {
		s(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем SEVERE
	 * @param th виключна ситуація для логування
	 */
	public static void s(Throwable th) {
		s(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем SEVERE.
	 * Наприклад: <code>Log.s("Test {0}", new String[] {"severe."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void s(String msg, Object[] params) {
		s(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем SEVERE.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void s(String msg, Throwable th) {
		s(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем WARNING.
	 * Наприклад: <code>Log.s("Test {0}", ex, new String[] {"severe."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void s(String msg, Throwable th, Object[] params) {
		l(Level.SEVERE, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем CONFIG">
	/**
	 * Залогувати повідомлення з рівнем CONFIG
	 * @param msg повідомлення для логування
	 */
	public static void c(String msg) {
		c(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем CONFIG
	 * @param th виключна ситуація для логування
	 */
	public static void c(Throwable th) {
		c(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем CONFIG.
	 * Наприклад: <code>Log.c("Test {0}", new String[] {"config."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void c(String msg, Object[] params) {
		c(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем CONFIG.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void c(String msg, Throwable th) {
		c(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем CONFIG.
	 * Наприклад: <code>Log.c("Test {0}", ex, new String[] {"config."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void c(String msg, Throwable th, Object[] params) {
		l(Level.CONFIG, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем ALL">
	/**
	 * Залогувати повідомлення з рівнем ALL
	 * @param msg повідомлення для логування
	 */
	public static void a(String msg) {
		a(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем ALL
	 * @param th виключна ситуація для логування
	 */
	public static void a(Throwable th) {
		a(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем ALL.
	 * Наприклад: <code>Log.a("Test {0}", new String[] {"all."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void a(String msg, Object[] params) {
		a(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем ALL.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void a(String msg, Throwable th) {
		a(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем ALL.
	 * Наприклад: <code>Log.a("Test {0}", ex, new String[] {"all."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void a(String msg, Throwable th, Object[] params) {
		l(Level.ALL, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем FINE">
	/**
	 * Залогувати повідомлення з рівнем FINE
	 * @param msg повідомлення для логування
	 */
	public static void f(String msg) {
		f(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем FINE
	 * @param th виключна ситуація для логування
	 */
	public static void f(Throwable th) {
		f(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем FINE.
	 * Наприклад: <code>Log.f("Test {0}", new String[] {"fine."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void f(String msg, Object[] params) {
		f(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем FINE.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void f(String msg, Throwable th) {
		f(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем FINE.
	 * Наприклад: <code>Log.f("Test {0}", ex, new String[] {"fine."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void f(String msg, Throwable th, Object[] params) {
		l(Level.FINE, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем FINEST">
	/**
	 * Залогувати повідомлення з рівнем FINEST
	 * @param msg повідомлення для логування
	 */
	public static void t(String msg) {
		t(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем FINEST
	 * @param th виключна ситуація для логування
	 */
	public static void t(Throwable th) {
		t(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем FINEST.
	 * Наприклад: <code>Log.t("Test {0}", new String[] {"finest."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void t(String msg, Object[] params) {
		t(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем FINEST.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void t(String msg, Throwable th) {
		t(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем FINEST.
	 * Наприклад: <code>Log.t("Test {0}", ex, new String[] {"finest."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void t(String msg, Throwable th, Object[] params) {
		l(Level.FINEST, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем FINER">
	/**
	 * Залогувати повідомлення з рівнем FINER
	 * @param msg повідомлення для логування
	 */
	public static void r(String msg) {
		r(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем FINER
	 * @param th виключна ситуація для логування
	 */
	public static void r(Throwable th) {
		r(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем FINER.
	 * Наприклад: <code>Log.r("Test {0}", new String[] {"finer."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void r(String msg, Object[] params) {
		r(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем FINER.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void r(String msg, Throwable th) {
		r(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем FINER.
	 * Наприклад: <code>Log.r("Test {0}", ex, new String[] {"finer."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void r(String msg, Throwable th, Object[] params) {
		l(Level.FINER, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем OFF">
	/**
	 * Залогувати повідомлення з рівнем OFF
	 * @param msg повідомлення для логування
	 */
	public static void o(String msg) {
		o(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем OFF
	 * @param th виключна ситуація для логування
	 */
	public static void o(Throwable th) {
		o(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем OFF.
	 * Наприклад: <code>Log.o("Test {0}", new String[] {"off."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void o(String msg, Object[] params) {
		o(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем OFF.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void o(String msg, Throwable th) {
		o(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем OFF.
	 * Наприклад: <code>Log.o("Test {0}", ex, new String[] {"off."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void o(String msg, Throwable th, Object[] params) {
		l(Level.OFF, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з рівнем INFO">
	/**
	 * Залогувати повідомлення з рівнем INFO
	 * @param msg повідомлення для логування
	 */
	public static void i(String msg) {
		i(msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію з рівнем INFO
	 * @param th виключна ситуація для логування
	 */
	public static void i(Throwable th) {
		i(null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення, з рівнем INFO.
	 * Наприклад: <code>Log.i("Test {0}", new String[] {"info."});</code>
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void i(String msg, Object[] params) {
		i(msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням з рівнем INFO.
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void i(String msg, Throwable th) {
		i(msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього, з рівнем INFO.
	 * Наприклад: <code>Log.i("Test {0}", ex, new String[] {"info."});</code>
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void i(String msg, Throwable th, Object[] params) {
		l(Level.INFO, msg, th, params);
	}
	//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Логування з вказаним рівнем">
	/**
	 * Залогувати повідомлення
	 * @param lvl рівень логування
	 * @param msg повідомлення для логування
	 */
	public static void l(Level lvl,String msg) {
		l(lvl, msg, null, null);
	}

	/**
	 * Залогувати виключну ситуацію
	 * @param lvl рівень логування
	 * @param th виключна ситуація для логування
	 */
	public static void l(Level lvl, Throwable th) {
		l(lvl, null, th, null);
	}

	/**
	 * Залогувати повідомлення, з вказаними параметрами для підстановки
	 * в повідомлення.
	 * Наприклад: <code>Log.l(Level.WARNING, "Test {0}", new String[] {"warning."});</code>
	 * @param lvl рівень логування
	 * @param msg повідомлення для логування
	 * @param params масив параметрів для підстановки в повідомлення.
	 */
	public static void l(Level lvl, String msg, Object[] params) {
		l(lvl, msg, null, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням.
	 * @param lvl рывень логування
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 */
	public static void l(Level lvl, String msg, Throwable th) {
		l(lvl, msg, th, null);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього.
	 * Наприклад: <code>Log.l(Level.WARNING, "Test {0}", ex, new String[] {"warning."});</code>
	 * @param lvl рівень логування
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void l(Level lvl, String msg, Throwable th, Object[] params) {
		l(null, lvl, msg, th, params);
	}

	/**
	 * Залогувати виключну ситуацію з пояснювальним повідомленням та параметрами
	 * для підстановки в нього.
	 * Наприклад: <code>Log.l(Log.class, Level.WARNING, "Test {0}", ex, new String[] {"warning."});</code>
	 * @param className - Ім’я класу для визначення логувальника
	 * @param lvl рівень логування
	 * @param msg повідомлення, опис виключної ситуаціі
	 * @param th виключна ситуація для логування
	 * @param params параметри для підстановки в повідомлення
	 */
	public static void l(String className, Level lvl, String msg, Throwable th, Object[] params) {
		StackTraceElement ste = foundCallerTraceElement();
		LogRecord logRecord = new LogRecord(lvl, msg);
		logRecord.setSourceClassName(ste.getClassName());
		logRecord.setSourceMethodName(ste.getMethodName());
		if (th != null) logRecord.setThrown(th);
		if (params != null) logRecord.setParameters(params);
		Logger.getLogger(className != null && !className.isEmpty() ?
				  className : ste.getClassName()).log(logRecord);
	}

	/**
	 * Метод для пошуку елементу трейсу що представляє об’єкт котрий викликав метод данного класу
	 * @return елемент трейсу котрий представляє об’єкт, котрий викликав метод данного классу
	 */
	private static StackTraceElement foundCallerTraceElement() {
		int i = 0;
		StackTraceElement ste;
		StackTraceElement[] stes = new Throwable().getStackTrace();
		do {
			ste = stes[i];
			i++;
		} while (ste.getClassName().equals(Log.class.getName()));
		return ste;
	}
//</editor-fold>

	//<editor-fold defaultstate="collapsed" desc="Допоміжні методи логування.">

	/**
	 * Логування входу в метод.
	 */
	public static void ent() {
		StackTraceElement ste = foundCallerTraceElement();
		Logger.getLogger(ste.getClassName()).entering(ste.getClassName(), ste.getMethodName());
	}

	/**
	 * Логування входу в метод.
	 * @param pars параметри передані методу
	 */
	public static void ent(Object[] pars) {
		StackTraceElement ste = foundCallerTraceElement();
		Logger.getLogger(ste.getClassName()).entering(ste.getClassName(), ste.getMethodName(), pars);
	}

	/**
	 * Логування виходу з методу.
	 */
	public static void ext() {
		StackTraceElement ste = foundCallerTraceElement();
		Logger.getLogger(ste.getClassName()).exiting(ste.getClassName(), ste.getMethodName());
	}

	/**
	 * Логування виходу з методу.
	 * @param value значення котре повертає метод
	 */
	public static void ext(Object value) {
		StackTraceElement ste = foundCallerTraceElement();
		Logger.getLogger(ste.getClassName()).exiting(ste.getClassName(), ste.getMethodName(), value);
	}

	/**
	 * Логування генерування виключної ситуаціі
	 * @param th згенерована виключна ситуація
	 */
	public static void thr(Throwable th) {
		StackTraceElement ste = foundCallerTraceElement();
		Logger.getLogger(ste.getClassName()).throwing(ste.getClassName(), ste.getMethodName(), th);
	}
//</editor-fold>
}
